---
title: 'origin | Cypress Documentation'
description: Visit multiple domains of different origin in a single test in Cypress.
sidebar_label: origin
e2eSpecific: true
---

<ProductHeading product="app" />

# origin

Visit multiple different
[origins](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#definition_of_an_origin)
in a single test.

In normal use, a single Cypress test may only run commands in a single origin, a
limitation determined by standard web security features of the browser. The
`cy.origin()` command allows your tests to bypass this limitation.

:::caution

<Icon name="exclamation-triangle" /> **Obstructive Third Party Code**

By default Cypress will search through the response streams coming from your
server on first party `.html` and `.js` files and replace code that matches
patterns commonly found in framebusting. When using the `cy.origin()` command,
the third party code may also need to be modified for framebusting techniques.
This can be enabled by setting the
[`experimentalModifyObstructiveThirdPartyCode`](/app/references/experiments)
flag to `true` in the Cypress configuration. More information about this
experimental flag can be found on our
[Cross Origin Testing](/app/guides/cross-origin-testing#Modifying-Obstructive-Third-Party-Code)
doc.

:::

:::info

<strong>Changes in Cypress [v14.0.0](/app/references/changelog#14-0-0)</strong>

Cypress no longer injects `document.domain` by default, which means `cy.origin()`
must now be used to navigate between any two origins in the same test, even if
the two origins are in the same superdomain.
This behavior can be disabled by setting the
[injectDocumentDomain](/app/references/configuration#injectDocumentDomain) configuration option to `true`,
to allow a smooth transition of tests to the new behavior.
This configuration option will be removed in a future version of Cypress.

:::

## Syntax

```js
cy.origin(url, callbackFn)
cy.origin(url, options, callbackFn)
```

### Usage

<Icon name="check-circle" color="green" /> **Correct Usage**

```js
const hits = getHits() // Defined elsewhere
// Run commands in secondary origin, passing in serializable values
cy.origin('https://example.cypress.io', { args: { hits } }, ({ hits }) => {
  // Inside callback baseUrl is https://example.cypress.io
  cy.visit('/history/founder')
  // Commands are executed in secondary origin
  cy.get('h1').contains('About our Founder')
  // Passed in values are accessed via callback args
  cy.get('#hitcounter').contains(hits)
})
// Even though we're outside the secondary origin block,
// we're still on cypress.io so return to baseUrl
cy.visit('/')
// Continue running commands on primary origin
cy.get('h1').contains('My cool site under test')
```

<Icon name="exclamation-triangle" color="red" /> **Incorrect Usage**

```js
const hits = getHits()
cy.visit('https://example.cypress.io/history/founder')
// To interact with cross-origin content, move this inside cy.origin() callback
cy.get('h1').contains('Kitchen Sink')
// Origin must be a precise match including scheme, subdomain and port, i.e. https://www.cypress.io
cy.origin('https://www.cypress.io', () => {
  cy.visit('/about-us')
  cy.get('h1').contains('About us')
  // Fails because downloads is not passed in via args
  cy.contains(downloads)
})
// Won't work because still on www.cypress.io
cy.get('h1').contains('Kitchen Sink')
```

### Arguments

<Icon name="angle-right" /> **origin _(String)_**

A string specifying the origin in which the callback is to be executed.
This should at the very least contain a hostname. It may also include the
scheme and port number. The path may be included, but is not necessary.
The hostname must precisely match that of the secondary origin, including
all subdomains. Query params are not supported. If no scheme is provided,
the scheme defaults to `https`.

This argument will be used in two ways:

1. It uniquely identifies a secondary origin in which the commands in the
   callback will be executed. Cypress will inject itself into this origin, and
   then send it code to evaluate in that origin, without violating the browser's
   same-origin policy.

2. It overrides the `baseUrl` configured in your
   [global configuration](/app/references/configuration#Global) while inside
   the callback. So `cy.visit()` and `cy.request()` will use this URL as a
   prefix, not the configured `baseUrl`.

<Icon name="angle-right" /> **options _(Object)_**

Pass in an options object to control the behavior of `cy.origin()`.

| option | description                                                                                                                                                                                                    |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| args   | Plain JavaScript object which will be serialized and sent from the primary origin to the secondary origin, where it will be deserialized and passed into the callback function as its first and only argument. |

:::caution

The `args` object is the **only** mechanism via which data may be injected into
the callback, the callback is **not** a closure and does not retain access to
the JavaScript context in which it was declared. Values passed into `args`
**must** be serializable.

:::

<Icon name="angle-right" /> **callbackFn _(Function)_**

The function containing the commands to be executed in the secondary origin.

This function will be stringified, sent to the Cypress instance in the secondary
origin and evaluated. If the `args` option is specified, the deserialized args
object will be passed into the function as its first and only argument.

There are a number of limitations placed on commands run inside the callback,
please see [Callback restrictions](#Callback-restrictions) section below for a
full list.

<HeaderYields />

- `cy.origin()` yields the value yielded by the last Cypress command in the
  callback function.
- If the callback contains no Cypress commands, `cy.origin()` yields the return
  value of the function.
- In either of the two cases above, if the value is not serializable, attempting
  to access the yielded value will throw an error.

## Examples

### Using dynamic data in a secondary origin

Callbacks are executed inside an entirely separate instance of Cypress, so
arguments must be transmitted to the other instance by means of
[the structured clone algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm).
The interface for this mechanism is the `args` option.

```js
const sentArgs = { username: 'username', password: 'P@55w0rd!' }
cy.origin(
  'supersecurelogons.com',
  // Send the args here...
  { args: sentArgs },
  // ...and receive them at the other end here!
  ({ username, password }) => {
    cy.visit('/login')
    cy.get('input#username').type(username)
    cy.get('input#password').type(password)
    cy.contains('button', 'Login').click()
  }
)
```

### Yielding a value

Values returned or yielded from the callback function **must** be serializable
or they will not be returned to the primary origin. For example, the following
will not work:

<Icon name="exclamation-triangle" color="red" /> **Incorrect Usage**

```js
cy.origin('https://example.cypress.io', () => {
  cy.visit('/')
  cy.get('h1') // Yields an element, which can't be serialized...
}).contains('Kitchen Sink') // ...so this will fail
```

Instead, you must explicitly yield a serializable value:

<Icon name="check-circle" color="green" /> **Correct Usage**

```js
cy.origin('https://example.cypress.io', () => {
  cy.visit('/')
  cy.get('h1').invoke('text') // Yields a string...
}).should('equal', 'Kitchen Sink') // 👍
```

### Navigating to secondary origin with cy.visit

When navigating to a secondary origin using `cy.visit()`, you can either
navigate prior to or after the `cy.origin` block. Errors are no longer thrown on
cross-origin navigation, but instead when commands interact with a cross-origin
page.

```js
// Do things in primary origin...

cy.origin('example.cypress.io', () => {
  // Visit https://example.cypress.io/history/founder
  cy.visit('/history/founder')
  cy.get('h1').contains('About our Founder')
})
```

Here the `baseUrl` inside the `cy.origin()` callback is set to `example.cypress.io`
and the protocol defaults to `https`. When `cy.visit()` is called with the path
`/history/founder`, the three are concatenated to make
`https://example.cypress.io/history/founder`.

#### Alternative navigation

```js
// Do things in primary origin...

cy.visit('https://example.cypress.io/history/founder')

// The cy.origin block is required to interact with the cross-origin page.
cy.origin('example.cypress.io', () => {
  cy.get('h1').contains('About our Founder')
})
```

Here the cross-origin page is visited prior to the `cy.origin` block, but any
interactions with the window are performed within the block which can
communicate with the cross-origin page

#### <Icon name="exclamation-triangle" color="red" /> Incorrect Usage

```js
// Do things in primary origin...

cy.visit('https://www.cypress.io/history/founder')

// This command will fail, it's executed on localhost but the application is at cypress.io
cy.get('h1').contains('About our Founder, Marvin Acme')
```

Here `cy.get('h1')` fails because we are trying to interact with a cross-origin
page outside of the cy.origin block, due to 'same-origin' restrictions, the
'localhost' javascript context can't communicate with 'cypress.io'.

### Navigating to secondary origin with UI

Navigating to a secondary origin by clicking a link or button in the primary
origin is supported.

```js
// Button in primary origin goes to https://example.cypress.io
cy.contains('button', 'Go').click()

cy.origin('example.cypress.io', () => {
  // No cy.visit is needed as the button brought us here
  cy.get('h1').contains('CYPRESS')
})
```

### Navigating to multiple secondary origins in succession

Callbacks may **not** themselves contain `cy.origin()` calls, so when visiting
multiple origins, do so at the top level of the test.

```js
cy.origin('example.cypress.com', () => {
  cy.visit('/')
  cy.url().should('contain', 'example.cypress.com')
})

cy.origin('cypress-dx.com', () => {
  cy.visit('/')
  cy.url().should('contain', 'cypress-dx.com')
})
```

### SSO login custom command

A very common requirement is logging in to a site before running a test. If
login itself is not the specific focus of the test, it's good to encapsulate
this functionality in a `login`
[custom command](/api/cypress-api/custom-commands) so you don't have to
duplicate this login code in every test. Here's an idealized example of how to
do this with `cy.origin()`.

<Icon name="exclamation-triangle" color="#f0ad4e" /> **Inefficient Usage**

```js
Cypress.Commands.add('login', (username, password) => {
  // Remember to pass in arguments via `args`
  const args = { username, password }
  cy.origin('cypress.io', { args }, ({ username, password }) => {
    // Go to https://example.cypress.com/login
    cy.visit('/login')
    cy.contains('Username').find('input').type(username)
    cy.contains('Password').find('input').type(password)
    cy.get('button').contains('Login').click()
  })
  // Confirm we're back at the primary origin before continuing
  cy.url().should('contain', '/home')
})
```

Having to go through an entire login flow before every test is not very
performant. Up until now you could get around this problem by putting login code
in the first test of your file, then performing subsequent tests reusing the
same session.

However, this is no longer possible, since all session state is now cleared
between tests. So to avoid this overhead we recommend you leverage the
[`cy.session()`](/api/commands/session) command, which allows you to easily
cache session information and reuse it across tests. So now let's enhance our
custom login command with `cy.session()` for a complete syndicated login flow
with session caching and validation. No mocking, no workarounds, no third-party
plugins!

```js
Cypress.Commands.add('login', (username, password) => {
  const args = { username, password }
  cy.session(
    // Username & password can be used as the cache key too
    args,
    () => {
      cy.origin('cypress.io', { args }, ({ username, password }) => {
        cy.visit('/login')
        cy.contains('Username').find('input').type(username)
        cy.contains('Password').find('input').type(password)
        cy.get('button').contains('Login').click()
      })
      cy.url().should('contain', '/home')
    },
    {
      validate() {
        cy.request('/api/user').its('status').should('eq', 200)
      },
    }
  )
})
```

## Learn More

### How to Test Multiple Origins

<DocsVideo
  src="https://youtube.com/embed/Fohrq5GZSD8"
  title="How to test multiple domains or origins with Cypress"
/>

In this video we walk through how to test multiple origins in a single test. We
also look at how to use the `cy.session()` command to cache session information
and reuse it across tests.
The configuration option `experimentalSessionAndOrigin`, mentioned in the video, is not used
since [Cypress 12.0.0](https://docs.cypress.io/app/references/changelog#12-0-0)
and the associated functionality is enabled by default.

## Notes

### Serialization

When entering a `cy.origin()` block, Cypress injects itself at runtime, with all
your configuration settings, into the requested origin, and sets up
bidirectional communication with that instance. This coordination model requires
that any data sent from one instance to another be
[serialized](https://developer.mozilla.org/en-US/docs/Glossary/Serialization)
for transmission. It is very important to understand that variables **inside**
the callback are not shared with the scope **outside** the callback. For example
this will not work:

<Icon name="exclamation-triangle" color="red" /> **Incorrect Usage**

```js
const foo = 1
cy.origin('cypress.io', () => {
  cy.visit('/')
  // This line will throw a ReferenceError because
  // `foo` is not defined in the scope of the callback
  cy.get('input').type(foo)
})
```

Instead, the variable must be explicitly passed into the callback using the
`args` option:

<Icon name="check-circle" color="green" /> **Correct Usage**

```js
const foo = 1
cy.origin('cypress.io', { args: { foo } }, ({ foo }) => {
  cy.visit('/')
  // Now it will pass
  cy.get('input').type(foo)
})
```

Cypress uses
[the structured clone algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm)
to transfer the `args` option to the secondary origin. This introduces a number
of
[restrictions on the data which may be passed](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#things_that_dont_work_with_structured_clone)
into the callback.

### Dependencies / Sharing Code

Within the `cy.origin()` callback,
[`Cypress.require()`](/api/cypress-api/require) can be utilized to include
[npm](https://www.npmjs.com/) packages and other files. It is functionally the
same as using
[CommonJS `require()`](https://nodejs.org/api/modules.html#requireid)
in browser-targeted code.

Note that it is not possible to use
[CommonJS `require()`](https://nodejs.org/api/modules.html#requireid)
or
[ES module `import()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import)
within the callback.

:::caution

Using `Cypress.require()` within the callback requires enabling the
[`experimentalOriginDependencies`](/app/references/experiments) option in the
Cypress configuration.

:::

Read more in the [`Cypress.require()` doc](/api/cypress-api/require) itself.

#### Example

```js
cy.origin('cypress.io', () => {
  const _ = Cypress.require('lodash')
  const utils = Cypress.require('../support/utils')

  // ... use lodash and utils ...
})
```

#### Custom commands

This makes it possible to share custom commands between tests run in primary and
secondary origins. We recommend this pattern for setting up your
[support file](/app/core-concepts/writing-and-organizing-tests#Support-file)
and setting up custom commands to run within the `cy.origin()` callback:

`cypress/support/commands.js`:

```js
Cypress.Commands.add('clickLink', (label) => {
  cy.get('a').contains(label).click()
})
```

`cypress/support/e2e.js`:

```js
// makes custom commands available to all Cypress tests in this spec,
// outside of cy.origin() callbacks
import './commands'

// code we only want run per test, so it shouldn't be run as part of
// the execution of cy.origin() as well
beforeEach(() => {
  // ... code to run before each test ...
})
```

`cypress/e2e/spec.cy.js`:

```js
before(() => {
  // makes custom commands available to all subsequent cy.origin('cypress.io)
  // calls in this spec. put it in your support file to make them available to
  // all specs
  cy.origin('cypress.io', () => {
    Cypress.require('../support/commands')
  })
})

it('tests cypress.io', () => {
  cy.origin('cypress.io', () => {
    cy.visit('/page')
    cy.clickLink('Click Me')
  })
})
```

#### Shared execution context

The JavaScript execution context is persisted between `cy.origin()` callbacks
that share the same origin. This can be utilized to share code between
successive `cy.origin()` calls.

```js
before(() => {
  cy.origin('cypress.io', () => {
    // makes commands defined in this file available to all callbacks
    // for cypress.io
    Cypress.require('../support/commands')
  })
})

it('uses cy.origin() + custom command', () => {
  cy.origin('cypress.io', () => {
    cy.visit('/page')
    cy.clickLink('Click Me')
  })
})

it('also uses cy.origin() + custom command', () => {
  cy.origin('cypress.io', () => {
    cy.visit('/page')
    cy.clickLink('Click Me')
  })

  cy.origin('cypress-dx.com', () => {
    // WARNING: cy.clickLink() will not be available because it is a
    // different origin
  })
})
```

### Callback restrictions

Because of the way in which the callback is transmitted and executed, there are
certain limitations on what code may be run inside it. In particular, the
following Cypress commands will throw errors if used in the callback:

- `cy.origin()`
- [`cy.intercept()`](/api/commands/intercept)
- [`cy.session()`](/api/commands/session)

### Other limitations

There are other testing scenarios which are not currently covered by
`cy.origin()`:

- It cannot run commands
  [in a different browser window](/app/references/trade-offs#Multiple-browsers-open-at-the-same-time)
- It cannot run commands in a different browser tab
- It cannot run commands
  [inside an `<iframe>` element](/app/faq#How-do-I-test-elements-inside-an-iframe)

## History

| Version                                     | Changes                                                                                                     |
| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| [14.0.0](/app/references/changelog#14-0-0)  | `cy.origin()` is now required when navigating between origins in the same test, rather than superdomains.   |
| [12.6.0](/app/references/changelog#10-7-0)  | Support for `Cypress.require()` added and support for CommonJS `require()` and ES module `import()` removed |
| [10.11.0](/app/references/changelog#10-7-0) | Support for CommonJS `require()` and ES module `import()` added and support for `Cypress.require()` removed |
| [10.7.0](/app/references/changelog#10-7-0)  | Support for `Cypress.require()` added                                                                       |

## See also

- [Easily test multi-domain workflows with cy.origin](https://cypress.io/blog/2022/04/25/cypress-9-6-0-easily-test-multi-domain-workflows-with-cy-origin/)
- [Custom Commands](/api/cypress-api/custom-commands)
- [`Cypress.require()`](/api/cypress-api/require)
- [`cy.session()`](/api/commands/session)
- [`cy.visit()`](/api/commands/visit)
